#include "ai_reflex.h"



void reflex_algorithm::get_commands()
{
	//clears current commands queue (not necessary)
	commands.clear();
	std::cout << "AI BEGIN" << std::endl;

	state = game::get_instance()->get_current_state()->clone();
	time = state->plrs[ state->current_player_id ]->get_time();
	//for estimating, how much time it will take to get all units to safe places
	int delta_t = time;

	check_dangerous_places();
	//now time == remaining time

	state->plrs[ state->current_player_id ]->set_time( time );
	delta_t = delta_t - time;
	//delta_t == time needed to get all units to safe places

	time = 0;


	check_attackable_places();
	//now time = cost of commands to go back

	delta_t += time;
	//delta_time = cost of commands to go back + defensive commands computet at the beginning

	for ( std::list< std::string >::iterator it = fire_commands.begin(); it != fire_commands.end(); ++it )
		commands.push_back( *it );
	
	move_commands.clear();

	//increases time by delta_t because these commands were only simulated, but not done
	state->plrs[ state->current_player_id ]->set_time( state->plrs[ state->current_player_id ]->get_time() + delta_t );

	check_dangerous_places();

	for ( std::list< std::string >::iterator it = move_commands.begin(); it != move_commands.end(); ++it )
		commands.push_back( *it );
	
	commands.push_back( "NXT" );
	move_commands.clear();
	fire_commands.clear();
	delete state;
	std::cout << "AI END" << std::endl;
};



std::pair< int, int > reflex_algorithm::find_safe_place( unit* u, game_state* state )
{
	unsigned int d = 30000;
	int tm;
	unsigned int dang = state->dangerous_places_nobody->get( u->get_x(), u->get_y() );
	std::pair< int, int > s_place = std::make_pair< int, int >( -1, -1 );
	for ( unsigned int i = 0; i < mapa::get_instance()->get_size_x(); ++i )
		for ( unsigned int j = 0; j < mapa::get_instance()->get_size_y(); ++j )
		{
			if ( ( state->map->get( i, j ) < 2000 ) && ( ( dynamic_cast< unit* >( state->objects->get( i, j ) ) ) == 0 ) )
			{
				//same danger, but closer
				if ( state->dangerous_places_nobody->get( i, j ) == dang )
				{
					tm = help_computes::ai_test_move( u->get_x(), u->get_y(), i, j, state );
					if ( ( tm != -1 ) && ( tm < ( int )d ) && ( u->get_player()->get_time() >= tm * ( int )u->get_cost_move() ) )
					{
						s_place = std::make_pair< unsigned int, unsigned int >( i, j );
						d = ( unsigned int )tm;
					}
				}
				//less dangerous
				else if ( state->dangerous_places_nobody->get( i, j ) < dang )
				{			
					int tm = help_computes::ai_test_move( u->get_x(), u->get_y(), i, j, state );
					if ( ( tm != -1 ) && ( u->get_player()->get_time() >= tm * ( int )u->get_cost_move() ) )
					{
						s_place = std::make_pair< unsigned int, unsigned int >( i, j );
						d = tm;
						dang = state->dangerous_places_nobody->get( i, j );
					}
				}
			}
		}

	return s_place;
};



void reflex_algorithm::check_dangerous_places()
{
	game_state* test_state = state->clone();
	for ( unsigned int i = 0; i < test_state->plrs[ test_state->current_player_id ]->units.size(); ++i )
	{
		unit* u = test_state->plrs[ test_state->current_player_id ]->units[ i ];
		//unit is in danger
		if ( test_state->dangerous_places_nobody->get( u->get_x(), u->get_y() ) > 0 )
		{
			std::cout << "Unit at [" << u->get_x() << "," << u->get_y() << "] is in danger - ";
			std::pair< int, int > place = find_safe_place( u, test_state );
			//safe place found
			if ( place.first != -1 )
			{
				string cmd = game::create_command( "MOV", u->get_id(), place.first, place.second );
				if ( help_computes::apply_command( test_state, cmd ) )
				{
					move_commands.push_back( cmd );
					delete test_state->dangerous_places_nobody;
					test_state->dangerous_places_nobody = help_computes::map_places_dangerous_nobody( test_state );
					std::cout << "new place: [" << place.first << "," << place.second << "]" << std::endl;
				}
				else
				{
					std::cout << "no new place found" << std::endl;
				}
			}
			else
			{
				std::cout << "no new place found" << std::endl;
			}
		}
	}
	time = test_state->plrs[ test_state->current_player_id ]->get_time();
	delete test_state;
};



std::pair< int, int > reflex_algorithm::find_best_target_unit( unit* u, game_state* state )
{
	std::pair< int, int > targ = std::make_pair< int, int >( -1, -1 );
	int hlth = 30000;
	for ( unsigned int i = 0; i < state->players_count; ++i )
	{
		if ( i == state->current_player_id )
			continue;
		//finds the opponent's unit with lowest health
		for ( unsigned int j = 0; j < state->plrs[ i ]->units.size(); ++j )
		{
			unit* tu = state->plrs[ i ]->units[ j ];
			if ( tu->get_health() < hlth )
			{
				int d = help_computes::ai_test_fire( u->get_x(), u->get_y(), tu->get_x(), tu->get_y(), state );			
				if ( ( d != -1 ) && ( d <= ( int )u->get_range() ) )
				{
					hlth = tu->get_health();
					targ.first = tu->get_x();
					targ.second = tu->get_y();
				}
			}
		}
	}
	return targ;
};



std::pair< int, std::pair< int, int > > reflex_algorithm::find_best_target( game_state* state )
{
	std::pair< int, std::pair< int, int > > comm = std::make_pair< int, std::pair< int, int > >( -1, std::make_pair< int, int >( -1, -1 ) );
	int hlth = 30000;
	//finds unit and target with the lowest health
	for ( unsigned int i = 0; i < state->plrs[ state->current_player_id ]->units.size(); ++i )
	{
		unit* u = state->plrs[ state->current_player_id ]->units[ i ];
		std::pair< int, int > temp_targ;
		temp_targ = find_best_target_unit( u, state );
		if ( ( temp_targ.first != -1 ) && ( ( dynamic_cast< unit* >( state->objects->get( temp_targ.first, temp_targ.second ) ) )->get_health() < hlth ) )
		{
			hlth = ( dynamic_cast< unit* >( state->objects->get( temp_targ.first, temp_targ.second ) ) )->get_health();
			comm.first = u->get_id();
			comm.second = temp_targ;
		}
	}
	return comm;
};



bool reflex_algorithm::can_anybody_attack( game_state* state )
{
	for ( unsigned int i = 0; i < state->players_count; ++i )
	{
		if ( i == state->current_player_id )
			continue;
		for ( unsigned int j = 0; j < state->plrs[ i ]->units.size(); ++j )
		{
			unit* u = state->plrs[ i ]->units[ j ];
			if ( state->attackable_places->get( u->get_x(), u->get_y() ) > 0 )
				return true;
		}
	}
	return false;
};



std::pair < double, std::pair< int, int > > reflex_algorithm::find_better_place_unit( unit* u, game_state* state )
{
	std::pair < double, std::pair< int, int > > place = std::make_pair< double, std::pair< int, int > >( -1, std::make_pair< int, int >( -1, -1 ) );
	//dist = lowest dist to an opponent
	double dist = sqrt( pow ( ( float )( mapa::get_instance()->get_size_x() ), 2 ) + pow ( ( float )( mapa::get_instance()->get_size_y() ), 2 ) );// + 30000;
	for ( unsigned int i = 0; i < state->players_count; ++i )
	{
		if ( i == state->current_player_id )
			continue;
		for ( unsigned int j = 0; j < state->plrs[ i ]->units.size(); ++j )
		{
			unit* tu = state->plrs[ i ]->units[ j ];
			//xtemp_dist = move distance
			int xtemp_dist = help_computes::ai_test_move_nobody( u->get_x(), u->get_y(), tu->get_x(), tu->get_y(), state );
			if ( xtemp_dist == -1 )
				xtemp_dist = 30000;
			//temp_dist = air line distance
			double temp_dist = sqrt( pow ( ( float )( ( int )tu->get_x() - ( int )u->get_x() ), 2 ) + pow ( ( float )( ( int )tu->get_y() - ( int )u->get_y() ), 2 ) );// + xtemp_dist;
			//closer opponent found
			if ( temp_dist < dist )
			{
				//try to move left
				if ( ( u->get_x() > 0 ) && ( help_computes::ai_test_move( u->get_x(), u->get_y(), u->get_x() - 1, u->get_y(), state ) != -1 ) )
				{
					int xtd = help_computes::ai_test_move_nobody( u->get_x() - 1, u->get_y(), tu->get_x(), tu->get_y(), state );
					if ( xtd == -1 )
						xtd = 30000;
					double td = sqrt( pow ( ( float )( ( int )tu->get_x() - ( int )u->get_x() + 1 ), 2 ) + pow ( ( float )( ( int )tu->get_y() - ( int )u->get_y() ), 2 ) );// + xtd;
					if ( td <= temp_dist )
					{
						dist = td;
						place.first = td;
						place.second.first = u->get_x() - 1;
						place.second.second = u->get_y();
					}
				}
				//try to move right
				if ( ( u->get_y() > 0 ) && ( help_computes::ai_test_move( u->get_x(), u->get_y(), u->get_x(), u->get_y() - 1, state ) != -1 ) )
				{
					int xtd = help_computes::ai_test_move_nobody( u->get_x(), u->get_y() - 1, tu->get_x(), tu->get_y(), state );
					if ( xtd == -1 )
						xtd = 30000;
					double td = sqrt( pow ( ( float )( ( int )tu->get_x() - ( int )u->get_x() ), 2 ) + pow ( ( float )( ( int )tu->get_y() - ( int )u->get_y() + 1 ), 2 ) );// + xtd;
					if ( td <= temp_dist )
					{
						dist = td;
						place.first = td;
						place.second.first = u->get_x();
						place.second.second = u->get_y() - 1;
					}
				}
				//try to move up
				if ( ( u->get_x() < mapa::get_instance()->get_size_x() - 1 ) && ( help_computes::ai_test_move( u->get_x(), u->get_y(), u->get_x() + 1, u->get_y(), state ) != -1 ) )
				{
					int xtd = help_computes::ai_test_move_nobody( u->get_x() + 1, u->get_y(), tu->get_x(), tu->get_y(), state );
					if ( xtd == -1 )
						xtd = 30000;
					double td = sqrt( pow ( ( float )( ( int )tu->get_x() - ( int )u->get_x() - 1 ), 2 ) + pow ( ( float )( ( int )tu->get_y() - ( int )u->get_y() ), 2 ) );// + xtd;
					if ( td <= temp_dist )
					{
						dist = td;
						place.first = td;
						place.second.first = u->get_x() + 1;
						place.second.second = u->get_y();
					}
				}
				//try to move down
				if ( ( u->get_y() < mapa::get_instance()->get_size_y() ) && ( help_computes::ai_test_move( u->get_x(), u->get_y(), u->get_x(), u->get_y() + 1, state ) != -1 ) )
				{
					int xtd = help_computes::ai_test_move_nobody( u->get_x(), u->get_y() + 1, tu->get_x(), tu->get_y(), state );
					if ( xtd == -1 )
						xtd = 30000;
					double td = sqrt( pow ( ( float )( ( int )tu->get_x() - ( int )u->get_x() ), 2 ) + pow ( ( float )( ( int )tu->get_y() - ( int )u->get_y() - 1 ), 2 ) );// + xtd;
					if ( td <= temp_dist )
					{
						dist = td;
						place.first = td;
						place.second.first = u->get_x();
						place.second.second = u->get_y() + 1;
					}
				}
			}
		}
	}
	return place;
};



std::pair< int, std::pair< int, int > > reflex_algorithm::find_better_place( game_state* state )
{
	std::pair< int, std::pair< int, int > > comm = std::make_pair< int, std::pair< int, int > >( -1, std::make_pair< int, int >( -1, -1 ) );
	double dist = sqrt( pow ( ( float )( mapa::get_instance()->get_size_x() ), 2 ) + pow ( ( float )( mapa::get_instance()->get_size_y() ), 2 ) );
	//finds unit, that can get closest to the opponent
	for ( unsigned int i = 0; i < state->plrs[ state->current_player_id ]->units.size(); ++i )
	{
		unit* u = state->plrs[ state->current_player_id ]->units[ i ];
		std::pair < double, std::pair< int, int > > res = find_better_place_unit( u, state );
		if ( res.first != -1 )
		{
			if ( res.first < dist )
			{
				dist = res.first;
				comm.first = u->get_id();
				comm.second = res.second;
			}
		}
	}
	return comm;
};



void reflex_algorithm::find_target_or_move( game_state* state )
{
	
	if ( can_anybody_attack( state ) )
	{
		std::pair< int, std::pair< int, int > > comm = find_best_target( state );
		//target found
		if ( comm.first != -1 )
		{
			string cmd = game::create_command( "ATT", comm.first, comm.second.first, comm.second.second );
			if ( help_computes::apply_command( state, cmd ) )
			{
				fire_commands.push_back( cmd );
				delete state->dangerous_places;
				delete state->dangerous_places_nobody;
				delete state->attackable_places;
				state->dangerous_places = help_computes::map_places_dangerous( state );
				state->dangerous_places_nobody = help_computes::map_places_dangerous_nobody( state );
				state->attackable_places = help_computes::map_places_attackable( state );
				unit* au = state->plrs[ state->current_player_id ]->units[ comm.first ];
				std::cout << "Unit at [" << au->get_x() << "," << au->get_y() << "] will attack to [" << comm.second.first << "," << comm.second.second << "]" << std::endl;
				//recursive call
				find_target_or_move( state );
			}
		}
	}
	else
	{
		std::pair< int, std::pair< int, int > > comm = find_better_place( state );
		//closer place found
		if ( comm.first != -1 )
		{
			//temp_time == actual time
			int temp_time = state->plrs[ state->current_player_id ]->get_time();
			string cmd = game::create_command( "MOV", comm.first, comm.second.first, comm.second.second );
			if ( help_computes::apply_command( state, cmd ) )
			{
				if ( state->dangerous_places->get( comm.second.first, comm.second.second ) > 0 )
				{
					//sets time = time - cost_move, so unit can get back
					state->plrs[ state->current_player_id ]->set_time( state->plrs[ state->current_player_id ]->get_time() - ( temp_time - state->plrs[ state->current_player_id ]->get_time() ) );
					//time += cost of movement back
					time += ( temp_time - state->plrs[ state->current_player_id ]->get_time() );
				}
				fire_commands.push_back( cmd );
				delete state->dangerous_places;
				delete state->dangerous_places_nobody;
				delete state->attackable_places;
				state->dangerous_places = help_computes::map_places_dangerous( state );
				state->dangerous_places_nobody = help_computes::map_places_dangerous_nobody( state );
				state->attackable_places = help_computes::map_places_attackable( state );
				unit* au = state->plrs[ state->current_player_id ]->units[ comm.first ];
				std::cout << "Unit at [" << au->get_x() << "," << au->get_y() << "] will move to [" << comm.second.first << "," << comm.second.second << "]" << std::endl;
				//recursive call
				find_target_or_move( state );
			}
		}
	}
};



void reflex_algorithm::check_attackable_places()
{
	game_state* test_state = state->clone();
	find_target_or_move( test_state );
	delete state;
	state = test_state;
};



